﻿using DG.Tweening;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;


namespace PP.Basic
{
    public static class SchedulerExtension
    {
        /// <summary>
        /// 计划一个时间任务回调，并返回该计时任务对象
        /// </summary>
        /// <param name="o">
        /// 绑定目标对象，可以是 GameObject、Transform、Behaviour、Component、任意脚本对象。
        /// 若目标对象为 null，则为全局回调；若目标对象不为 null，则当它被销毁时，自动取消计划任务
        /// </param>
        /// <param name="handler">回调方法</param>
        /// <param name="interval">时间间隔</param>
        /// <param name="repeat">重复次数，-1: 无限循环</param>
        /// <param name="autoKillOnCompletion">是否在完成时自动销毁，默认true</param>
        /// <returns>Tween schedule， 计划对象</returns>
        public static Tween Schedule(this UnityEngine.Object o, float interval, Action handler, int repeat = -1, bool autoKillOnCompletion = true)
        {
            return Scheduler.Instance.Schedule(interval, handler, repeat, o, autoKillOnCompletion);
        }

        /// <summary>
        /// Schedule Update. interval = 0f
        /// </summary>
        public static Tween Schedule(this UnityEngine.Object o, Action handler, int repeat = -1, bool autoKillOnCompletion = true)
        {
            return Scheduler.Instance.Schedule(0f, handler, repeat, o, autoKillOnCompletion);
        }

        /// <summary>
        /// 延时调用
        /// </summary>
        /// <param name="o">
        /// 绑定目标对象，可以是 GameObject、Transform、Behaviour、Component、任意脚本对象。
        /// 若目标对象为 null，则为全局回调；若目标对象不为 null，则当它被销毁时，自动取消计划任务
        /// </param>
        /// <param name="delay">延时时间</param>
        /// <param name="handler">回调方法</param>
        /// <param name="autoKillOnCompletion">是否在完成时自动销毁，默认true</param>
        public static Tween DelayedCall(this UnityEngine.Object o, float delay, Action handler, bool autoKillOnCompletion = true)
        {
            return Scheduler.Instance.DelayedCall(delay, handler, o, autoKillOnCompletion);
        }

        /// <summary>
        /// 延时0.1f后调用. delay = 0.1f
        /// </summary>
        public static Tween DelayedCall(this UnityEngine.Object o, Action handler, bool autoKillOnCompletion = true)
        {
            return Scheduler.Instance.DelayedCall(0.1f, handler, o, autoKillOnCompletion);
        }

        /// <summary>
        /// 一帧后调用
        /// </summary>
        public static Tween ScheduleForNextFrame(this UnityEngine.Object o, Action handler, bool autoKillOnCompletion = true)
        {
            return Scheduler.Instance.DelayedCall(Time.deltaTime, handler, o, autoKillOnCompletion);
        }

        /// <summary>
        /// 指定帧数后调用
        /// </summary>
        public static Tween ScheduleForFrames(this UnityEngine.Object o, int frameCount, Action handler, bool autoKillOnCompletion = true)
        {
            return Scheduler.Instance.DelayedCall(Time.deltaTime * frameCount, handler, o, autoKillOnCompletion);
        }

        /// <summary>
        /// 按帧时间为间隔，待定直到条件满足时，再调用回调
        /// </summary>
        /// <param name="o">任务绑定对象</param>
        /// <param name="predicate">条件判断器</param>
        /// <param name="handler">回调</param>
        /// <returns>计划任务对象</returns>
        public static Tween ScheduleForUtilFrame(this UnityEngine.Object o, Func<bool> predicate, Action handler, bool autoKillOnCompletion = true)
        {
            Tween task = null;
            task = Scheduler.Instance.Schedule(Time.deltaTime, ()=> 
            {
                // 条件不满足, 继续等待
                if (!predicate())
                    return;

                // 条件满足, 取消计划
                task.Kill();
                task = null;

                // 执行回调
                handler?.Invoke();
            }, -1, o, autoKillOnCompletion);
            return task;
        }

        /// <summary>
        /// 按帧时间为间隔，待定直到条件不再满足时，再调用回调
        /// </summary>
        /// <param name="o">任务绑定对象</param>
        /// <param name="predicate">条件判断器</param>
        /// <param name="handler">回调</param>
        /// <returns>计划任务对象</returns>
        public static Tween ScheduleForWhileFrame(this UnityEngine.Object o, Func<bool> predicate, Action handler, bool autoKillOnCompletion = true)
        {
            Tween task = null;
            task = Scheduler.Instance.Schedule(Time.deltaTime, () =>
            {
                // 条件满足, 继续等待
                if (predicate())
                    return;

                // 条件不再满足, 取消计划
                task.Kill();
                task = null;

                // 执行回调
                handler?.Invoke();
            }, -1, o, autoKillOnCompletion);
            return task;
        }

        /// <summary>
        /// 按帧时间为间隔，待定直到 RebuildLayout 完成时，再调用回调
        /// 在UI回调中常用，当前已经处于Rebuild流程中时，不能修改UI，否则会破坏UI渲染流程，导致无法操作。
        /// 所以需要使用此方法，等待RebuildLayout完成后，才能进一步执行修改。
        /// </summary>
        /// <param name="o">任务绑定对象</param>
        /// <param name="handler">回调</param>
        /// <returns>计划任务对象</returns>
        public static Tween ScheduleForRebuildLayoutComplete(this UnityEngine.Object o, Action handler, bool autoKillOnCompletion = true)
        {
            return o.ScheduleForUtilFrame(() => !CanvasUpdateRegistry.IsRebuildingLayout(), handler, autoKillOnCompletion);
        }

        /// <summary>
        /// 取消此计划
        /// </summary>
        /// <param name="o"></param>
        /// <param name="task"></param>
        public static void Unschedule(this UnityEngine.Object o, Tween task)
        {
            Scheduler.Instance.Unschedule(task);
        }

        /// <summary>
        /// 取消绑定在目标对象上的计划
        /// </summary>
        /// <param name="o"></param>
        public static void Unschedule(this UnityEngine.Object o)
        {
            Scheduler.Instance.Unschedule(o);
        }
    }
}
